import { _decorator, sys, Asset, resources} from 'cc';
import {ClientEvent} from "../framework/clientEvent";
import {Constant} from "../framework/constant";
// import { JKWRouter } from '../sdk/jkwRouter';

/**
 * Predefined variables
 * Name = MgobeUtil
 * DateTime = Fri Dec 17 2021 15:45:07 GMT+0800 (中国标准时间)
 * Author = yly1234560
 * FileBasename = mgobeUtil.ts
 * FileBasenameNoExtension = mgobeUtil
 * URL = db://assets/scripts/mgobeUtil.ts
 * ManualUrl = https://docs.cocos.com/creator/3.3/manual/zh/
 *
 */


const MGOBE_GAME_ID = 'obg-etob8wxf';
const MGOBE_URL = 'etob8wxf.wxlagame.com';
const MGOBE_GAME_KEY = "7747014c03cc166e0134c3b6bb8f16d014fd32ec";

export class MgobeUtil{
    private static _instance: MgobeUtil = null!;

    private _room: MGOBE.Room = null!;
    private _cacertNativeUrl: string = "";
    private _openId: string = "";
    private _name: string = "";
    private _headUrl: string = "";
    private _frames: MGOBE.types.Frame[] = [];
    private _lastFrameId: number = 0;

    public static get instance () {
        if (this._instance) {
            return this._instance;
        }

        this._instance = new MgobeUtil();
        return this._instance;
    }


    /**
     * 初始化Mgobe
     * @param openId 玩家唯一标示符
     * @param name 玩家昵称
     * @param headUrl 玩家头像
     * @param callback 回调函数
     * @returns
     */
    public initSDK(openId: string, name: string, headUrl: string, callback: Function) {
        if (this.isInited()) {
            callback && callback();
            return console.log("SDK 已经初始化，无需重复操作");
        }
        this._openId = openId;
        this._name = name;
        this._headUrl = headUrl;
        console.log("正在初始化 SDK");
        if (sys.isNative && !this._cacertNativeUrl) {
            // CA 根证书（Cocos Native 环境下 wss 需要此参数）
            return resources.load("/cacert", Asset, (err, asset) => {
                console.log("加载证书结束 " + (!err));
                if (err) {
                    return;
                }
                this._cacertNativeUrl = asset.nativeUrl;
                this.initSDK(openId, name, headUrl, callback);
            });
        }
        this._initMgobe((event: {code: number}) => {
            if (event.code === MGOBE.ErrCode.EC_OK) {
                console.log("初始化 SDK 成功");
                this._setLister();
            } else {
                console.error(`初始化 SDK 失败，错误码：${event.code}, 请前往对战服务开通地址:https://cloud.tencent.com/document/product/1038/33299`)
            }
            callback && callback();
        })
    }

    /**
     * 加入房间
     * @param roomId 房间号
     */
    public joinRoom(roomId: string| null = null) {
        this._getMyRoom((event: {code: number, data: { roomInfo: MGOBE.types.RoomInfo}}) => {
            if (event && event.code === 0) { //已在房间
                this._getRoomDetail((event: {code: number, msg:string, data: { roomInfo: MGOBE.types.RoomInfo}}) => {
                    // if (event && event.code === 0) {
                    //     if (roomId) {
                    //         JKWRouter.instance.notifyJoinRoom(event.code, event.msg);
                    //     } else {
                    //         JKWRouter.instance.notifyCreateRoom(event.code, event.msg, event.data.roomInfo.id);
                    //     }
                    //     //避免上线后状态多次转变
                    //     // this.changeCustomPlayerStatus(Constant.CUSTOM_PLAYER_STATUS.CONNECTED);
                    //     // ClientEvent.dispatchEvent(Constant.EVENT_NAME.ON_GET_ROOM_INFO, event.data.roomInfo, MGOBE.Player.id);
                    // } else if (roomId) {
                    //     JKWRouter.instance.notifyJoinRoom(event.code, event.msg);
                    // } else {
                    //     JKWRouter.instance.notifyCreateRoom(event.code, event.msg, "");
                    // }
                })
                // UIManager.instance.showTips(Constant.ROOM_TIPS.IN_ROOM);
                return  console.log("玩家已在房间内：", event.data.roomInfo.name);
            }

            if (event && event.code === 20011 && roomId) { //加入房间
                this._joinRoom(roomId, (event: {code: number, msg:string, data: { roomInfo: MGOBE.types.RoomInfo}}) => {
                    // if (event && event.code === 0) {
                    //     // ClientEvent.dispatchEvent(Constant.EVENT_NAME.ON_GET_ROOM_INFO, event.data.roomInfo, MGOBE.Player.id);
                    // } else {
                    //     UIManager.instance.showTips(Constant.ROOM_TIPS.JOIN_ROOM_ERROR);
                    // }
                    // JKWRouter.instance.notifyJoinRoom(event.code, event.msg);
                });

                return console.log("加入房间号", roomId);
            }

            if (event && event.code === 20011) { //创建房间
                this._createRoom((event: {code: number, msg:string, data: { roomInfo: MGOBE.types.RoomInfo}}) => {
                    // if (event && event.code === 0) {
                    //     JKWRouter.instance.notifyCreateRoom(event.code, event.msg, event.data.roomInfo.id);
                    //     // ClientEvent.dispatchEvent(Constant.EVENT_NAME.ON_GET_ROOM_INFO, event.data.roomInfo, MGOBE.Player.id);
                    // } else {
                    //     UIManager.instance.showTips(Constant.ROOM_TIPS.CREATE_ROOM_ERROR);
                    //     JKWRouter.instance.notifyCreateRoom(event.code, event.msg, "");
                    // }
                });
                return console.log("创建新房间");
            }
        });
    }

    /**
     * 改变玩家自定义状态
     * @param status 玩家状态
     * @returns
     */
    public changeCustomPlayerStatus (status: number) {
        if (!this._room) return;
        const changeCustomPlayerStatusPara = {
            customPlayerStatus: status,
        };
        this._room.changeCustomPlayerStatus(changeCustomPlayerStatusPara, event => console.log('changeCustomPlayerStatus', event));
    }


    /**
     * 退出房间
     * @returns
     */
    public leaveRoom () {
        if (!this._room) return;
        console.log('leaveRoom');
        this._room.leaveRoom({}, event => console.log('leaveRoom', event));
    }

    /**
     * 开始帧同步
     */
    public startFrameSync () {
        if (!this._room) return;
        if (this._room.roomInfo.owner !== MGOBE.Player.id || this._room.roomInfo.frameSyncState === Constant.FRAME_SYNC_STATE.START) {
            return;
        }

        this._room.startFrameSync({} ,  event => console.log('startFrameSync', event));

    }

    /**
     * 检查是否是房主
     */
    public checkIsRoomOwner (id: string) {
        if (!this._room) return false;
        return this._room.roomInfo.owner === id;
    }

    /**
     * 结束帧同步
     */
    public stopFrameSync () {
        if (!this._room) return;
        this._room.stopFrameSync({} ,  event => console.log('stopFrameSync', event));
    }

    /**
     * 发送帧数据
     * @param info 帧数据
     */
    public sendFrame (info: object) {
        if (!this._room || this._room.roomInfo.frameSyncState === Constant.FRAME_SYNC_STATE.STOP) return;
        this._room.sendFrame({data: info },  event => {
            // console.log('sendFrame', event)
        });
    }

    /**
     * 发送消息给房间内玩家
     * @param info 消息
     */
    public sendToClient (info: object) {
        if (!this._room) return;
        const sendToClientPara = {
            recvPlayerList: null,
            msg: JSON.stringify(info),
            recvType: 2
        };
        this._room.sendToClient(sendToClientPara,  event => {
            // console.log('sendFrame', event)
        });
    }

    /**
     * 接受到消息
     *
     * @param event
     * @private
     */
    private _onRecvFromClient(event: MGOBE.types.BroadcastEvent<MGOBE.types.RecvFromClientBst>){
        if (!this._room) return;
        ClientEvent.dispatchEvent(Constant.EVENT_NAME.ON_RECV_FROM_CLIENT,event.data.sendPlayerId, event.data.msg);
    }

    /**
     * 获取随机值
     * @returns 随机值
     */
    random() {
        return MGOBE.RandomUtil.random();
    }

    /**
     * 初始化Mgobe
     * @param callback 回调函数
     */
    private _initMgobe(callback: Function) {
        const defaultGameInfo = {
            gameId: MGOBE_GAME_ID,
            openId: this._openId,
            secretKey: MGOBE_GAME_KEY,
        };
        const defaultConfig = {
            url: MGOBE_URL,
            isAutoRequestFrame: true,
            cacertNativeUrl: this._cacertNativeUrl,
        };

        // MGOBE.DebuggerLog.enable = true;
        // if (sys.isNative) {
        //     MGOBE.DebuggerLog.enable = false;
        // }
        this._room = new MGOBE.Room();
        // 初始化
        MGOBE.Listener.init(defaultGameInfo, defaultConfig, (event: {}) => {
            console.log(event);
            callback && callback(event);
        });
    }

    /**
     * 获取我的房间信息
     * @param callback 回调函数
     * @returns
     */
    private _getMyRoom(callback: Function) {
        if (!this._room) return callback(null);
        MGOBE.Room.getMyRoom(event => {
            console.log('getMyRoom', event)
            callback && callback(event);
        });
    }

    /**
     * 获取房间详细信息
     * @param callback 回调函数
     * @returns
     */
    private _getRoomDetail(callback: Function) {
        if (!this._room) return callback(null);
        this._room.getRoomDetail(event => {
            console.log('getRoomDetail', event)
            callback && callback(event);
        });
    }

    /**
     * 创建房间
     * @param callback 创建房间回调函数
     * @returns
     */
    private _createRoom(callback: Function) {
        if (!this._room) return callback(null);
        const playerInfo = {
            name: this._name || "1",
            customPlayerStatus: Constant.CUSTOM_PLAYER_STATUS.CONNECTED,
            customProfile: this._headUrl || "",
        };
        const createRoomPara = {
            roomName: Constant.GAME_NAME,
            maxPlayers: Constant.MAX_PLAYER,
            roomType: "PK",
            isPrivate: false,
            customProperties: "",
            playerInfo,
        };
        this._room.createRoom(createRoomPara, (event: object) => {
            console.log('createRoom', event)
            callback && callback(event);
        });

    }

    /**
     * 根据房间号获取房间信息
     * @param roomId 房间号
     * @param callback 回调数据
     * @returns
     */
    private _getRoomByRoomId(roomId: string, callback: Function) {
        if (!this._room) return callback(null);
        const roomInfo = {
            roomId: roomId
        }
        MGOBE.Room.getRoomByRoomId(roomInfo, (event: object) => {
            console.log('getRoomByRoomId', event);
            callback && callback(event);
        });

    }

    /**
     * 加入房间
     * @param roomId 房间号
     * @param callback 回调函数
     * @returns
     */
    private _joinRoom(roomId: string, callback: Function) {
        if (!this._room) return callback(null);
        this._getRoomByRoomId(roomId, (event: {code: number, data: { roomInfo: MGOBE.types.RoomInfo}})  => {
            if (event.code === 0) {
                // 设置房间信息到 room 实例
                this._room.initRoom(event.data.roomInfo);
                const playerInfo = {
                    name: this._name || "2",
                    customPlayerStatus: Constant.CUSTOM_PLAYER_STATUS.CONNECTED,
                    customProfile: this._headUrl || "",
                };
                const joinRoomPara = {
                    playerInfo,
                };
                this._room.joinRoom(joinRoomPara, (event: object) =>{
                    console.log('joinRoom', event);
                    callback && callback(event);
                });
            }
        })
    }

    /**
     * 判断是否初始化
     * @returns
     */
    public isInited() {
        // 初始化成功后才有玩家ID
        return !!MGOBE.Player && !!MGOBE.Player.id;
    }

    /**
     * 设置房间相关监听
     * @returns
     */
    private _setLister() {
        if (!this._room) return;
        MGOBE.Listener.add(this._room);
        this._room.onUpdate  = this._onUpdate.bind(this);
        this._room.onStartFrameSync = this._onStartFrameSync.bind(this);
        this._room.onStopFrameSync = this._onStopFrameSync.bind(this);
        this._room.onRecvFrame = this._onRecvFrame.bind(this);
        this._room.onAutoRequestFrameError = this._onAutoRequestFrameError.bind(this);

        // yly
        this._room.onRecvFromClient = this._onRecvFromClient.bind(this);
    }

    /**
     * 房间信息更新回调
     * @param room 房间信息
     */
    private _onUpdate(room?: MGOBE.Room | undefined) {
        console.log("***房间信息更新回调 _onUpdate************");
        if (room) {
            console.log("onUpdate", JSON.stringify(room.roomInfo));
            ClientEvent.dispatchEvent(Constant.EVENT_NAME.ON_GET_ROOM_INFO, room.roomInfo, MGOBE.Player.id);
        }
    }

    /**
     * 开始帧同步回调函数
     * @param event
     */
    private _onStartFrameSync(event: MGOBE.types.BroadcastEvent<MGOBE.types.StartFrameSyncBst>) {
        console.log("onStartFrameSync", JSON.stringify(event));
        this._frames = [];
        this.changeCustomPlayerStatus(Constant.CUSTOM_PLAYER_STATUS.GAME);
        ClientEvent.dispatchEvent(Constant.EVENT_NAME.ON_START_FRAME_SYNC, event.data.roomInfo.startGameTime);
    }

    /**
     * 结束帧同步回调函数
     * @param event
     */
    private _onStopFrameSync(event: MGOBE.types.BroadcastEvent<MGOBE.types.StopFrameSyncBst>) {
        console.log("onStopFrameSync", JSON.stringify(event));
        // this.changeCustomPlayerStatus(Constants.CUSTOM_PLAYER_STATUS.CONNECTED);
        // ClientEvent.dispatchEvent(Constants.EVENT_NAME.ON_STOP_FRAME_SYNC);

    }

    /**
     * 接受帧数据回调
     * @param event 帧数据
     */
    private _onRecvFrame(event: MGOBE.types.BroadcastEvent<MGOBE.types.RecvFrameBst>) {
        // console.log("_onRecvFrame", JSON.stringify(event));

        let frame: MGOBE.types.Frame = event.data.frame;
        MGOBE.RandomUtil.init(frame.ext.seed);
        this._handleFrame(frame);
    }

    /**
     * 自动补帧失败回调
     * @param event 自动补帧失败回调信息
     */
    private _onAutoRequestFrameError(event: MGOBE.types.BroadcastEvent<MGOBE.types.ResponseEvent<MGOBE.types.RequestFrameRsp>>) {
        console.log("onAutoRequestFrameError", event.data.code);
        // 重试
        //    this._retryAutoRequestFrame();
        this._requestFrame();
    }

    /**
     * 请求自动补帧
     * @returns
     */
    private _retryAutoRequestFrame () {
        if (!this._room) return;
        this._room.retryAutoRequestFrame();
    }


    /**
     * 手动补帧
     * @returns
     */
    private _requestFrame () {
        if (!this._room) return;
        const requestFramePara = {
            beginFrameId: this._lastFrameId + 1,
            endFrameId: Math.floor((Date.now() - this._room.roomInfo.startGameTime * 1000) / 1000 * this._room.roomInfo.frameRate),
        };
        this._room.requestFrame(requestFramePara, (event: MGOBE.types.ResponseEvent<MGOBE.types.RequestFrameRsp>)=> {
            console.log('requestFrame: ' + event.code);
            if (event.code === 0) {
                event.data?.frames.forEach((value: MGOBE.types.Frame) => {
                    this._handleFrame(value);
                });
            }
        });
    }

    /**
     * 处理帧数据
     * @param frame
     * @returns
     */
    private _handleFrame(frame: MGOBE.types.Frame) {
        if (!frame.id) return;
        let result = this._frames.filter((value: MGOBE.types.Frame) => {
            return value.id === frame.id;
        });
        if (result.length) return;
        this._frames.push(frame);
        result = this._frames.filter((value: MGOBE.types.Frame) => {
            return value.id === frame.id - 1;
        });
        if (!result.length && frame.id !== 1) {
            this._requestFrame();
            return;
        }
        result = this._frames.filter((value: MGOBE.types.Frame) => {
            return value.id >= frame.id;
        });
        result.sort((a: MGOBE.types.Frame, b: MGOBE.types.Frame) => {
            return a.id - b.id;
        })
        this._lastFrameId = result[result.length - 1].id;
        // console.log(this._lastFrameId);
        ClientEvent.dispatchEvent(Constant.EVENT_NAME.ON_RECV_SYNC, result);
    }

    /**
     * 打印操作帧
     * @param frame
     * @returns
     */
    public printActionFrame(){
        let result = this._frames.filter((value: MGOBE.types.Frame) => {
            return value.items.length;
        });
        result.sort((a: MGOBE.types.Frame, b: MGOBE.types.Frame) => {
            return a.id - b.id;
        });
        console.log('action:' + result.length);
        console.log(result);
    }

    /**
     * 获取当前帧率
     * @returns 帧率
     */
    public getFrameRate(){
        if (!this._room) return 15;
        return this._room.roomInfo.frameRate;
    }

    // update (deltaTime: number) {
    //     // [4]
    // }
}

/**
 * [1] Class member could be defined like this.
 * [2] Use `property` decorator if your want the member to be serializable.
 * [3] Your initialization goes here.
 * [4] Your update function goes here.
 *
 * Learn more about scripting: https://docs.cocos.com/creator/3.3/manual/zh/scripting/
 * Learn more about CCClass: https://docs.cocos.com/creator/3.3/manual/zh/scripting/ccclass.html
 * Learn more about life-cycle callbacks: https://docs.cocos.com/creator/3.3/manual/zh/scripting/life-cycle-callbacks.html
 */

//@ts-ignore
window.printFrameInfo = () => {
    MgobeUtil.instance.printActionFrame()
};

//@ts-ignore
window.printGameState = () => {
    ClientEvent.dispatchEvent(Constant.EVENT_NAME.ON_DEBUG_GAME_STATE);
};